package cn.frontop.funswitch.service.impl;

import cn.frontop.funswitch.common.CommonConstant;
import cn.frontop.funswitch.common.Result;
import cn.frontop.funswitch.dao.IndexConfigDao;
import cn.frontop.funswitch.entity.*;
import cn.frontop.funswitch.enums.RequestEnum;
import cn.frontop.funswitch.exception.FunSwitchException;
import cn.frontop.funswitch.param.TestApiVo;
import cn.frontop.funswitch.service.*;
import cn.frontop.funswitch.util.CommonUtil;
import cn.frontop.funswitch.util.JavaScriptUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jayway.jsonpath.JsonPath;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;


/**
 * @author Yun
 */
@Service("indexConfigService")
public class IndexConfigServiceImpl extends ServiceImpl<IndexConfigDao, IndexConfigEntity> implements IndexConfigService {

    @Autowired
    TokenService tokenService;
    @Autowired
    ProjectService projectService;
    @Autowired
    ApiPathService apiPathService;
    @Autowired
    SocketConfigService socketConfigService;

    @SuppressWarnings({"unchecked", "SuspiciousMethodCalls"})
    @Override
    public Object getIndex(Integer indexId, JSONObject params) {
        IndexConfigEntity indexConfigEntity = this.getById(indexId);
        CommonUtil.isEmpty(indexConfigEntity, "指标不存在!!!");
        ProjectEntity project = projectService.getById(indexConfigEntity.getProjectId());
        CommonUtil.isEmpty(project, "项目不存在!!!");
        //判断是不是静态类型 静态不需要往下走请求流程
        //获取token信息
        String token = ObjectUtil.isNull(indexConfigEntity.getTokenId()) ? "" : tokenService.getTokenById(indexConfigEntity.getTokenId());
        //获取请求信息
        RequestDto requestDto = new RequestDto();
        requestDto.setUrl(replace(project.getDomian() + indexConfigEntity.getUrl(), params));
        //token
        HashMap<String, Object> hashMap = StrUtil.isEmpty(project.getTokenKey()) ? new HashMap<>() : new HashMap<String, Object>((params == null || params.isEmpty()) ? 1 : params.size() + 1) {{
            put(project.getTokenKey(), token);
        }};
        //动态参数
        if (!StrUtil.isEmpty(indexConfigEntity.getParam())) {
            JSONArray dynamicParameters = JSONUtil.parseArray(indexConfigEntity.getParam());
            dynamicParameters.stream().map(String::valueOf).forEach(t -> hashMap.put(t, params.get(t) == null ? "" : params.get(t)));
        }
        //拼接请求
        String apply = CommonUtil.apply(project.getHeader(), indexConfigEntity.getPrivateHeader(), hashMap);
        //替换body
        requestDto.setBody(replace(indexConfigEntity.getBody(), params, StrUtil.toString(params)));
        requestDto.setHeaders(replace(apply, params));
        String conversion = RequestEnum.valueOf(indexConfigEntity.getRequestType().toUpperCase()).conversion(requestDto);
        if (!JSONUtil.isTypeJSON(conversion) || !JsonPath.read(conversion, "$." + project.getCodeKey()).toString().trim().equals(String.valueOf(project.getCode()))) {
            return Result.error("第三方返回错误信息,请检查配置! 返回信息:" + conversion);
        }
        if (StrUtil.isNotEmpty(indexConfigEntity.getJsonPath()) && !JSONUtil.isTypeJSON(JsonPath.read(conversion, indexConfigEntity.getJsonPath()).toString())) {
            return JsonPath.read(conversion, indexConfigEntity.getJsonPath()).toString();
        }
        if (!StrUtil.isEmpty(indexConfigEntity.getJsScript())) {
            return JavaScriptUtil.execution(indexConfigEntity.getJsScript(), conversion, JSONUtil.toJsonStr(params));
        }
        JSONArray jsonArray = CommonUtil.toJsonArray(StrUtil.isEmpty(indexConfigEntity.getJsonPath()) ? conversion : JsonPath.read(conversion, indexConfigEntity.getJsonPath()).toString());
        if (jsonArray.isEmpty()) {
            return jsonArray;
        }
        CommonUtil.isEmpty(jsonArray, Result.ResultEnum.ERROR_CODE.getRespDesc());
        if (indexConfigEntity.getType() == null) {
            return jsonArray;
        }
        JSONArray exclude = JSONUtil.parseArray(StrUtil.isEmpty(indexConfigEntity.getExclude()) ? jsonArray.getJSONObject(0).keySet() : indexConfigEntity.getExclude());
        JSONObject mapKey = JSONUtil.parseObj(StrUtil.isEmpty(indexConfigEntity.getMapKey()) ? "{}" : indexConfigEntity.getMapKey());
        //返回值
        return getResult(indexConfigEntity, jsonArray, exclude, mapKey);
    }

    @Override
    public Object callBack(Integer indexId, JSONObject body) {
        IndexConfigEntity indexConfigEntity = this.getById(indexId);
        if (indexConfigEntity == null) {
            return body;
        }
        if (!StrUtil.isEmpty(indexConfigEntity.getJsScript())) {
            return JavaScriptUtil.execution(indexConfigEntity.getJsScript(), JSONUtil.toJsonStr(body), null);
        }
        JSONArray jsonArray = new JSONArray();
        jsonArray.add(body);
        jsonArray = StrUtil.isEmpty(indexConfigEntity.getJsonPath()) ? jsonArray : CommonUtil.toJsonArray(JsonPath.read(body, indexConfigEntity.getJsonPath()).toString());
        CommonUtil.isEmpty(jsonArray, Result.ResultEnum.ERROR_CODE.getRespDesc());
        JSONArray exclude = JSONUtil.parseArray(StrUtil.isEmpty(indexConfigEntity.getExclude()) ? jsonArray.getJSONObject(0).keySet() : indexConfigEntity.getExclude());
        JSONObject mapKey = JSONUtil.parseObj(StrUtil.isEmpty(indexConfigEntity.getMapKey()) ? "{}" : indexConfigEntity.getMapKey());
        //返回值
        return getResult(indexConfigEntity, jsonArray, exclude, mapKey);
    }

    @Override
    public Object getIndexByEntity(IndexConfigEntity indexConfigEntity, JSONObject params) {
        CommonUtil.isEmpty(indexConfigEntity, "指标不存在!!!");
        ProjectEntity project = projectService.getById(indexConfigEntity.getProjectId());
        CommonUtil.isEmpty(project, "项目不存在!!!");
        //替换为mock数据
        params = JSONUtil.parseObj(indexConfigEntity.getMock());
        //获取token信息
        String token = ObjectUtil.isNull(indexConfigEntity.getTokenId()) ? null : tokenService.getTokenById(indexConfigEntity.getTokenId());
        //获取请求信息
        RequestDto requestDto = new RequestDto();
        requestDto.setUrl(params.isEmpty() ? project.getDomian() + indexConfigEntity.getUrl() : replace(project.getDomian() + indexConfigEntity.getUrl(), params));
        //拼接请求
        String apply = CommonUtil.apply(project.getHeader(), indexConfigEntity.getPrivateHeader(), token != null ? new HashMap<String, Object>(1) {{
            put(project.getTokenKey(), token);
        }} : new HashMap<>(1));
        //替换body
        requestDto.setBody(params.isEmpty() ? indexConfigEntity.getBody() : replace(indexConfigEntity.getBody(), params, StrUtil.toString(params)));
        requestDto.setHeaders(params.isEmpty() ? apply : replace(apply, params));
        String conversion = RequestEnum.valueOf(indexConfigEntity.getRequestType().toUpperCase()).conversion(requestDto);
        if (!JSONUtil.isTypeJSON(conversion) || !JsonPath.read(conversion, "$." + project.getCodeKey()).toString().trim().equals(String.valueOf(project.getCode()))) {
            return Result.error("第三方返回错误信息,请检查配置! 返回信息:" + conversion);
        }
        if (StrUtil.isNotEmpty(indexConfigEntity.getJsonPath()) && !JSONUtil.isTypeJSON(JsonPath.read(conversion, indexConfigEntity.getJsonPath()).toString())) {
            return JsonPath.read(conversion, indexConfigEntity.getJsonPath()).toString();
        }
        JSONArray jsonArray = CommonUtil.toJsonArray(StrUtil.isEmpty(indexConfigEntity.getJsonPath()) ? conversion : JsonPath.read(conversion, indexConfigEntity.getJsonPath()).toString());
        CommonUtil.isEmpty(jsonArray, "不应该为空结果!");
        if (indexConfigEntity.getType() == null) {
            return jsonArray;
        }
        JSONArray exclude = JSONUtil.parseArray(StrUtil.isEmpty(indexConfigEntity.getExclude()) ? jsonArray.getJSONObject(0).keySet() : indexConfigEntity.getExclude());
        JSONObject mapKey = JSONUtil.parseObj(StrUtil.isEmpty(indexConfigEntity.getMapKey()) ? "{}" : indexConfigEntity.getMapKey());
        //返回值
        return indexConfigEntity.getType() == null ? jsonArray : getResult(indexConfigEntity, jsonArray, exclude, mapKey);

    }

    @Override
    public Object testApi(JSONObject params) {
        ApiPathEntity apiPathEntity = apiPathService.getById(params.getInt(CommonConstant.ID));
        //判断是不是静态类型 静态不需要往下走请求流程
        if (apiPathEntity.getType().equals(4)) {
            return JSONUtil.isTypeJSON(apiPathEntity.getStaticData()) ? JSONUtil.parse(apiPathEntity.getStaticData()) : apiPathEntity.getStaticData();
        }
        CommonUtil.isEmpty(apiPathEntity, "api不存在!");
        IndexConfigEntity indexConfigEntity = this.getById(JSONUtil.isTypeJSONArray(apiPathEntity.getIndexId()) ? JSONUtil.parseArray(apiPathEntity.getIndexId()).getInt(0) : apiPathEntity.getIndexId());
        CommonUtil.isEmpty(indexConfigEntity, "指标不存在!");
        ProjectEntity project = projectService.getById(indexConfigEntity.getProjectId());
        CommonUtil.isEmpty(project, "项目不存在!");
        long start = System.currentTimeMillis();
        //mock参数
        JSONObject mock = StrUtil.isEmpty(indexConfigEntity.getMock()) ? new JSONObject() : JSONUtil.parseObj(indexConfigEntity.getMock());
        //获取token信息
        String token = ObjectUtil.isNull(indexConfigEntity.getTokenId()) ? null : tokenService.getTokenById(indexConfigEntity.getTokenId());
        //获取请求信息
        RequestDto requestDto = new RequestDto();
        requestDto.setUrl(mock.isEmpty() ? project.getDomian() + indexConfigEntity.getUrl() : replace(project.getDomian() + indexConfigEntity.getUrl(), mock));
        //拼接请求
        String apply = CommonUtil.apply(project.getHeader(), indexConfigEntity.getPrivateHeader(), token != null ? new HashMap<String, Object>(1) {{
            put(project.getTokenKey(), token);
        }} : new HashMap<>(1));
        //替换body
        requestDto.setBody(mock.isEmpty() ? requestDto.getBody() : replace(indexConfigEntity.getBody(), mock, StrUtil.toString(mock)));
        requestDto.setHeaders(mock.isEmpty() ? apply : replace(apply, mock));
        String conversion = RequestEnum.valueOf(indexConfigEntity.getRequestType().toUpperCase()).conversion(requestDto);
        TestApiVo testApiVo = new TestApiVo();
        testApiVo.setTime(System.currentTimeMillis() - start);
        testApiVo.setSuccess(false);
        if (JSONUtil.isTypeJSON(conversion)) {
            if (JsonPath.read(conversion, "$." + project.getCodeKey()).toString().trim().equals(String.valueOf(project.getCode()))) {
                testApiVo.setSuccess(true);
                JSONArray jsonArray = CommonUtil.toJsonArray(StrUtil.isEmpty(indexConfigEntity.getJsonPath()) ? conversion : JsonPath.read(conversion, indexConfigEntity.getJsonPath()).toString());
                if (indexConfigEntity.getType() != null) {
                    JSONArray exclude = JSONUtil.parseArray(StrUtil.isEmpty(indexConfigEntity.getExclude()) ? jsonArray.getJSONObject(0).keySet() : indexConfigEntity.getExclude());
                    JSONObject mapKey = JSONUtil.parseObj(StrUtil.isEmpty(indexConfigEntity.getMapKey()) ? "{}" : indexConfigEntity.getMapKey());
                    testApiVo.setCleanData(getResult(indexConfigEntity, jsonArray, exclude, mapKey));
                } else {
                    testApiVo.setCleanData(jsonArray);
                }
            }
        }
        testApiVo.setData(JSONUtil.isTypeJSON(conversion) ? JSONUtil.parse(conversion) : conversion);
        return testApiVo;
    }

    /**
     * 根据类型获取
     *
     * @param indexConfigEntity 指标配置
     * @param jsonArray         请求结果数据
     * @param exclude           过滤需要的key
     * @param mapKey            转换参数
     * @return 类型结果
     */
    private Object getResult(IndexConfigEntity indexConfigEntity, JSONArray jsonArray, JSONArray exclude, JSONObject mapKey) {
        // 0.普通类型(值过滤,key转换) 1.对象转集合() 2.过滤集合转对象 3.分组
        /**
         * 对象转集合 格式
         * common====>公共数据 每个都会添加
         * list====>单个对象 对应处理
         * join====>单个对象 另外添加的值
         * {
         * 	"list": [{
         * 		"key": "a",
         * 	    "value":"b",
         * 		"join": {
         * 			"a": 1
         *                }* 	}],
         * 	"common": {}
         * }
         */
        Object result;
        switch (indexConfigEntity.getType()) {
            case 0:
                result = jsonArray.stream().map(JSONObject::new).map(t -> {
                    HashMap<String, Object> hashMap = new HashMap<>(exclude.size());
                    exclude.stream().map(String::valueOf).forEach(key -> hashMap.put(mapKey.get(key) == null ? key : mapKey.getStr(key), t.get(key)));
                    return hashMap;
                }).collect(Collectors.toList());
                break;
            case 1:
                JSONObject jsonObject = jsonArray.getJSONObject(0);
                JSONArray conversionList = mapKey.getJSONArray(CommonConstant.LIST);
                HashMap<String, Object> commonMap = mapKey.getJSONObject(CommonConstant.COMMON) == null ? new HashMap<>(0) : JSONUtil.toBean(mapKey.getJSONObject(CommonConstant.COMMON), HashMap.class);
                result = conversionList.stream().map(JSONObject::new).map(t -> {
                    HashMap<String, Object> hashMap = new HashMap<>(16);
                    hashMap.put(t.getStr(CommonConstant.VALUE), jsonObject.get(t.getStr(CommonConstant.KEY)));
                    hashMap.putAll(t.get(CommonConstant.JOIN) == null ? new HashMap<>(0) : JSONUtil.toBean(t.getJSONObject(CommonConstant.JOIN), HashMap.class));
                    hashMap.putAll(commonMap);
                    return hashMap;
                }).collect(Collectors.toList());
                break;
            case 2:
                HashMap<String, Object> hashMap1 = new HashMap<>(2);
                hashMap1.put(CommonConstant.NAME, mapKey.get(CommonConstant.NAME));
                JSONObject jsonObject1 = mapKey.getJSONObject(CommonConstant.CONVERSION);
                hashMap1.put(CommonConstant.DATA, jsonArray.stream().map(JSONObject::new).map(t -> {
                    HashMap<String, Object> hashMap = new HashMap<>(exclude.size());
                    exclude.stream().map(String::valueOf).forEach(key -> hashMap.put(jsonObject1.get(key) == null ? key : jsonObject1.getStr(key), t.get(key)));
                    return hashMap;
                }).collect(Collectors.toList()));
                result = hashMap1;
                break;
            case 3:
                JSONObject conversionMap = mapKey.getJSONObject(CommonConstant.CONVERSION);
                //初步处理 进行分组
                Map<Object, List<HashMap<String, Object>>> collect = jsonArray.parallelStream().map(JSONObject::new).map(t -> {
                    HashMap<String, Object> hashMap = new HashMap<>(exclude.size());
                    exclude.stream().map(String::valueOf).forEach(key -> hashMap.put(conversionMap.get(key) == null ? key : conversionMap.getStr(key), t.get(key)));
                    return hashMap;
                }).collect(Collectors.groupingBy(t -> t.get(mapKey.get(CommonConstant.GROUPING_BY))));
                //转换成前端需要 格式 和Key
                result = collect.keySet().stream().map(t -> {
                    HashMap<String, Object> hashMap = new HashMap<>(2);
                    hashMap.put(CommonConstant.NAME, t);
                    hashMap.put(CommonConstant.DATA, collect.get(t));
                    return hashMap;
                }).collect(Collectors.toList());
                break;
            default:
                throw new FunSwitchException("不存在该转换类型!!!");
        }
        return result;
    }


    /**
     * 替换方法
     *
     * @param source 源数据
     * @param params 参数
     * @return 结果
     */
    private String replace(String source, JSONObject params) {
        if (params == null || params.isEmpty() || StrUtil.isEmpty(source)) {
            return source;
        }
        AtomicReference<String> result = new AtomicReference<>(source);
        params.keySet().forEach(t -> {
            String str = params.getObj(t).toString();
            if (str == null) {
                return;
            }
            String tmp = result.get().replace("${" + t + "}", str);
            result.set(tmp);
        });
        return result.get();
    }

    /**
     * 替换方法
     *
     * @param source 源数据
     * @param params 参数
     * @return 结果
     */
    private String replace(String source, JSONObject params, String defaultValue) {
        String replace = replace(source, params);
        return StrUtil.isEmpty(replace) ? defaultValue : replace;
    }
}